﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Clipping.Infrastructure.Models;
using Clipping.Utils;
using System.Threading.Tasks;
using Ionic.Zip;
using System.Xml;
using System.Net;
using System.Drawing;
using System.IO;

namespace ClippingEditor
{
    /// <summary>
    /// Interaction logic for MultiClippingUpload.xaml
    /// </summary>
    public partial class MultiClippingUpload : Window
    {
        Dictionary<string, FieldViewModel> _commonFieldViewModels;

        List<FieldViewModel> _extFieldViewModels = new List<FieldViewModel>();
        List<OptionalFieldViewModel> _extOptionalFieldViewModes = new List<OptionalFieldViewModel>();
        List<MultiSelectOptionalFieldViewModel> _extMultiSelectOptionalFieldViewModes = new List<MultiSelectOptionalFieldViewModel>();

        HashSet<string> commonFields = new HashSet<string>() { "headlineC", "link", "synopsisC", "mediaID", "Section", "mediaDate", "Reporter", "Words", "adValue", "Page", "Width", "Height", "pageSize" };
        private SysSettings sysSetting;
        private UserSettings userSetting;

        List<News> news;
        List<NewsModel> newsModels;
        Log4cb.ILog4cbHelper logHelper;
        private bool _fullchannel = false;//值为true时，提取频道形如 首页>资讯中心，否则形如： 资讯中心
        public MultiClippingUpload()
        {
            InitializeComponent();
        }
        public MultiClippingUpload(List<News> news, Log4cb.ILog4cbHelper logHelper = null)
            : this()
        {
            this.news = news;
            this.logHelper = logHelper;
            this.Loaded += new RoutedEventHandler(MultiClippingUpload_Loaded);
        }

        private void loadSettings()
        {
            SettingRepository rep = new SettingRepository();
            this.sysSetting = rep.GetSysSettings();
            this.userSetting = rep.GetUserSettings();
        }

        void MultiClippingUpload_Loaded(object sender, RoutedEventArgs e)
        {
            newsModels = new List<NewsModel>();
            foreach (var n in this.news)
            {
                newsModels.Add(new NewsModel(n));
            }
            this.dg.ItemsSource = newsModels;

            foreach (var n in newsModels)
            {
                if (Uri.IsWellFormedUriString(n.News.Url, UriKind.Absolute))
                {
                    Task.Factory.StartNew(getContent, n);
                }

            }
            loadSettings();
            this.categorySelector.SelectedClientChanged += new Action<ClientInfo>(categorySelector_SelectedClientChanged);


            generateFieldViewModels();
            bindFields();
        }



        private void generateFieldViewModels()
        {
            this._commonFieldViewModels = new Dictionary<string, FieldViewModel>();
            string[][] fields = new string[][] { new string[] { "标题", "headlineC" }, new string[] { "链接", "link" }, new string[] { "中文摘要", "synopsisC" } };
            foreach (var f in fields)
            {
                Field field = new Field() { DisplayName = f[0], Name = f[1], Type = FieldType.Field };
                FieldViewModel vm = new FieldViewModel(field, null);
                this._commonFieldViewModels.Add(field.Name, vm);
            }

            string[][] properties = new string[][] { new string[] { "版面", "Section" }, new string[] { "日期", "mediaDate" }, new string[] { "记者", "Reporter" }, new string[] { "字数", "Words" }, new string[] { "广告值", "adValue" }, new string[] { "页面", "Page" }, new string[] { "宽度", "Width" }, new string[] { "高度", "Height" }, new string[] { "页面大小", "pageSize" } };
            Field mediaField = new Field() { DisplayName = "媒体", Name = "mediaID", Type = FieldType.Property };
            MediaFieldViewModel mediaFieldVM = new MediaFieldViewModel(mediaField, null);

            this._commonFieldViewModels.Add(mediaField.Name, mediaFieldVM);

            foreach (var p in properties)
            {
                Field f = new Field() { DisplayName = p[0], Name = p[1], Type = FieldType.Property };

                FieldViewModel vm = new FieldViewModel(f, null);
                this._commonFieldViewModels.Add(f.Name, vm);
            }

            this._commonFieldViewModels["adValue"].Value = this.userSetting.DefaultAD;
            this._commonFieldViewModels["Width"].Value = this.userSetting.DefaultWidth;
            this._commonFieldViewModels["Height"].Value = this.userSetting.DefaultHeight;
            this._commonFieldViewModels["pageSize"].Value = this.userSetting.DefaultPageSize;
            this._commonFieldViewModels["Page"].Value = this.userSetting.DefaultPage;
        }

        private void bindFields()
        {
            Dictionary<string, FieldViewModel> viewModels = this._commonFieldViewModels;
            this.field_headline.DataContext = viewModels["headlineC"];
            this.field_link.DataContext = viewModels["link"];
            this.field_synopsisC.DataContext = viewModels["synopsisC"];
            this.field_media.DataContext = viewModels["mediaID"];
            this.field_section.DataContext = viewModels["Section"];
            this.field_date.DataContext = viewModels["mediaDate"];
            this.field_words.DataContext = viewModels["Words"];
            this.field_adValue.DataContext = viewModels["adValue"];
            this.field_page.DataContext = viewModels["Page"];
            this.field_reporter.DataContext = viewModels["Reporter"];
            this.field_width.DataContext = viewModels["Width"];
            this.field_height.DataContext = viewModels["Height"];
            this.field_pageSize.DataContext = viewModels["pageSize"];

        }








        private void getContent(object n)
        {
            getContent(n as NewsModel);
        }

        private void getContent(NewsModel n)
        {
            Uri uri = new Uri(n.News.Url);
            string channel;
            IHtmlDownload donwloader = new HTMLDownloader(true);
            string content = NewsParser.GetContentContinuously(uri, donwloader, out channel);
            n.News.Content = content;
            if (!string.IsNullOrEmpty(channel) && !_fullchannel)
            {
                int index = channel.LastIndexOf(">");
                if (index > 0 && index < channel.Length - 1)
                {
                    channel = channel.Substring(index + 1);
                    channel = channel.Trim();
                }
            }
            n.News.Channel = channel;

            DispatcherHelper.InvokeAction(() => { n.Status = "Ready"; n.IsReady = true; });
        }


        void categorySelector_SelectedClientChanged(ClientInfo client)
        {
            LoadExtFields(client);
        }


        private void LoadExtFields(ClientInfo client)
        {
            this.wrapPanel_ExtFields.Children.Clear();
            this._extFieldViewModels.Clear();
            this._extOptionalFieldViewModes.Clear();
            this._extMultiSelectOptionalFieldViewModes.Clear();
            if (client == null)
                return;
            if (client.Fields != null)
            {
                foreach (var field in client.Fields)
                {
                    if (!this.commonFields.Contains(field.Id))
                    {
                        FieldViewModel fvm = new FieldViewModel(field, null);
                        FieldView fv = new FieldView() { DataContext = fvm, Height = 30, MinWidth = 200 };
                        this._extFieldViewModels.Add(fvm);
                        this.wrapPanel_ExtFields.Children.Add(fv);
                    }
                }
            }
            if (client.Properties != null)
            {
                foreach (var opField in client.Properties)
                {
                    if (opField.MultiSelect == false)
                    {
                        OptionalFieldViewModel ofvm = new OptionalFieldViewModel(opField, null);
                        this._extOptionalFieldViewModes.Add(ofvm);
                        OptionalFieldView ofv = new OptionalFieldView() { DataContext = ofvm, Height = 30, MinWidth = 200 };
                        this.wrapPanel_ExtFields.Children.Add(ofv);
                    }
                    else
                    {
                        MultiSelectOptionalFieldViewModel ofvm = new MultiSelectOptionalFieldViewModel(opField, null);
                        this._extMultiSelectOptionalFieldViewModes.Add(ofvm);
                        MultiSelectOptionalFieldView ofv = new MultiSelectOptionalFieldView() { DataContext = ofvm };
                        this.stackPanel_ExtFields_MultiSelectFields.Children.Add(ofv);
                    }
                }
            }
        }



        public IEnumerable<News> ProcessedNews { get { return this.news; } }

        private void btn_Upload_Click(object sender, RoutedEventArgs e)
        {
            if (string.IsNullOrEmpty(sysSetting.UploadUrl))
            {
                MessageBox.Show("未指定上传剪报的HTTP，无法执行上传操作！");
                return;
            }
            if (categorySelector.SelectedCategories == null || categorySelector.SelectedCategories.Count == 0)
            {
                MessageBox.Show("未指定上传分类，无法执行上传操作！");
                return;
            }

            if (this.newsModels.Any(s => s.IsReady != true))
            {
                MessageBox.Show("请等待全部新闻的正文下载完毕再执行上传操作。");
            }

            foreach (var vm in this._extOptionalFieldViewModes)
            {
                if (vm.Value == null)
                {
                    MessageBox.Show(string.Format("请选择{0}", vm.Field.Name));
                    return;
                }
            }
            foreach (var news in this.newsModels)
            {
                Task.Factory.StartNew(uploadClipping, news);
            }
        }


        private void uploadClipping(object news)
        {
            uploadClipping(news as NewsModel);
        }
        private void uploadClipping(NewsModel news)
        {
            try
            {
                var clipping = generateClipping(news);
                string filename;
                if (saveClippingLocal(clipping, news.News.Url, out filename))
                {
                    upload(filename);
                    DispatcherHelper.InvokeAction(() => { news.Status = "完成"; news.Complete =true;});
                }
            }
            catch (Exception ex)
            {
                DispatcherHelper.InvokeAction(() => { news.Status = "失败";news.Complete =true; });
            }
            finally
            {
                if (this.newsModels.All(n => n.Complete))
                {
                    DispatcherHelper.InvokeAction(() => { report(); });
                }
            
            }
        }

        private void report()
        {
            if (this.newsModels.All(n => n.Status == "完成"))
            {
                if (MessageBox.Show("剪报上传成功，是否关闭窗体？", "上传成功", MessageBoxButton.YesNo, MessageBoxImage.Information) == MessageBoxResult.Yes)
                {
                    this.Close();
                }
            }
            else
            {
                MessageBox.Show("部分剪报上传失败。");
            }
        }

        private void upload(string fileName) 
        {
            try
            {
               var wc = new WebClient();
                if (this.sysSetting.UploadUrl.StartsWith("http", StringComparison.OrdinalIgnoreCase))
                {
                    string uploadurl = string.Format("{0}?user={1}&pwd={2}", this.sysSetting.UploadUrl, this.sysSetting.UploadUserName, this.sysSetting.UploadPassword);
                    wc.Credentials = new NetworkCredential(this.sysSetting.UploadUserName, this.sysSetting.UploadPassword);
                    wc.UploadFile(uploadurl, fileName);
                   }
                else if (this.sysSetting.UploadUrl.StartsWith("ftp", StringComparison.OrdinalIgnoreCase))
                {
                    FileInfo fi = new FileInfo(fileName);
                    string uploadurl = string.Format("{0}/{1}", this.sysSetting.UploadUrl, fi.Name);
                    wc.Credentials = new NetworkCredential(this.sysSetting.UploadUserName, this.sysSetting.UploadPassword);
                    wc.UploadFile(new Uri(uploadurl), fileName);
                }
                
            }
            catch (Exception ex)
            {
                this.logHelper.LogErrMsg(ex, "上传剪报失败");
            }
        }
        /// <summary>
        /// 生成剪报
        /// </summary>
        /// <param name="news"></param>
        /// <returns></returns>
        private XmlDataDocument generateClipping(NewsModel news)
        {
            string xmlTemplate = string.Format("<?xml version=\"1.0\" encoding=\"{0}\"?><Clipping><ContentHtml /><ContentTxt /><Pictures /><CapPictures /></Clipping>", this.sysSetting.Encoding);

            XmlDataDocument xmldoc = new XmlDataDocument();
            xmldoc.LoadXml(xmlTemplate);
            XmlElement xe;
            XmlNode rootnode = xmldoc.SelectSingleNode("/Clipping");
            xe = rootnode as XmlElement;
            xe.SetAttribute("ClientId", this.categorySelector.SelectedCategories.First().Client.Id);

            xe = xmldoc.CreateNode(XmlNodeType.Element, "Classes", "") as XmlElement;
            foreach (ClassInfo ci in this.categorySelector.SelectedCategories)
            {
                XmlNode subNode = xmldoc.CreateNode(XmlNodeType.Element, "Class", "");
                XmlNode valueNode = xmldoc.CreateNode(XmlNodeType.Text, "ValueClass", "");
                valueNode.Value = ci.Id;
                subNode.AppendChild(valueNode);
                xe.AppendChild(subNode);

            }
            rootnode.AppendChild(xe);

            foreach (var field in this._commonFieldViewModels)
            {

                if (field.Key == "link")
                {

                    var links = new List<string> { news.News.Url };
                    if (links != null)
                    {
                        foreach (string l in links)
                        {
                            XmlNode subNode = xmldoc.CreateNode(XmlNodeType.Element, "Link", "");
                            XmlNode valueNode = xmldoc.CreateNode(XmlNodeType.Text, "linkValue", "");
                            valueNode.Value = l;
                            subNode.AppendChild(valueNode);
                            xe.AppendChild(subNode);
                        }
                    }
                    rootnode.AppendChild(xe);
                }
                else
                {
                    xe = xmldoc.CreateNode(XmlNodeType.Element, "Field", "") as XmlElement;
                    xe.SetAttribute("Name", field.Value.Field.Name);

                    string pValue = "";
                    switch (field.Value.Field.Name)
                    {
                        case "headlineC"://标题
                            pValue = news.News.Title;
                            break;
                        case "synopsisC": //摘要
                            pValue = news.News.Summary;
                            break;
                        case "mediaID"://媒体
                            pValue = news.News.Media;
                            break;
                        case "mediaDate"://日期
                            pValue = news.News.PubDate;
                            break;
                        case "Section": //版面
                            pValue = news.News.Channel;
                            break;
                        case "Words"://字数
                            pValue = ClippingHelper.CountWords(news.News.Content).ToString();
                            break;
                        default:
                            pValue = field.Value.Value;
                            break;

                    }

                    xe.SetAttribute("Value", pValue);


                    rootnode.AppendChild(xe);
                }
            }

   
            foreach (var field in this._extFieldViewModels)
            {
                xe = xmldoc.CreateNode(XmlNodeType.Element, "Field", "") as XmlElement;
                xe.SetAttribute("Name", field.Field.Name);
                xe.SetAttribute("Value", field.Value);
                rootnode.AppendChild(xe);
            }
            foreach (var field in this._extOptionalFieldViewModes)
            {
                xe = xmldoc.CreateNode(XmlNodeType.Element, "Property", "") as XmlElement;
                xe.SetAttribute("Name", field.Field.Name);
                xe.SetAttribute("Value", field.Value.Id);
                rootnode.AppendChild(xe);
            }
            foreach (var field in this._extMultiSelectOptionalFieldViewModes)
            {
                xe = xmldoc.CreateNode(XmlNodeType.Element, "Property", "") as XmlElement;
                xe.SetAttribute("Name", field.Field.Name);
                var value = string.Join(",", field.Value.Select(v => v.Id).ToArray());
                xe.SetAttribute("Value", value);
                rootnode.AppendChild(xe);
            }




            xe = xmldoc.CreateNode(XmlNodeType.Element, "Field", "") as XmlElement;
            xe.SetAttribute("Name", "addedBy");
            xe.SetAttribute("Value", this.userSetting.UserName);
            rootnode.AppendChild(xe);

            string html = news.News.Content;
            if (html == null)
                html = "";
            string txt = "";
            if (!string.IsNullOrEmpty(html))
            {
                HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
                doc.LoadHtml(html);
                txt = doc.DocumentNode.InnerText;
            }
            XmlCDataSection cdata = xmldoc.CreateCDataSection(html);
            xmldoc.SelectSingleNode("/Clipping/ContentHtml").AppendChild(cdata);
            cdata = xmldoc.CreateCDataSection(txt);
            xmldoc.SelectSingleNode("/Clipping/ContentTxt").AppendChild(cdata);
            if (this.sysSetting.EmbedPic)
            {
                HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
                doc.LoadHtml(html);
                var imgNav = doc.CreateNavigator();
                var lis = imgNav.Select("//img");

                string link = news.News.Url;
                while (lis.MoveNext())
                {
                    string picUrl = string.Empty;
                    try
                    {
                        picUrl = lis.Current.GetAttribute("src", null);
                        string picbase64 = getBase64Pic(picUrl, link);
                        xe = xmldoc.CreateNode(XmlNodeType.Element, "Pic", "") as XmlElement;
                        XmlCDataSection picdata = xmldoc.CreateCDataSection(picbase64);
                        xe.SetAttribute("url", picUrl);
                        xe.AppendChild(picdata);
                        xmldoc.SelectSingleNode("/Clipping/Pictures").AppendChild(xe);
                    }
                    catch (Exception ex)
                    {
                        this.logHelper.LogWarnMsg(ex, "获取图片异常，网页：{1},图片：{0}", picUrl, link);
                    }
                }
            }
            return xmldoc;
        }


        private string getBase64Pic(string picUrl, string refererUrl)
        {
            if (string.IsNullOrEmpty(picUrl))
                return "";
            WebClient wc = new WebClient();
            wc.Headers.Add(HttpRequestHeader.Referer, refererUrl);
            wc.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.CacheIfAvailable);
            try
            {
                byte[] data = wc.DownloadData(picUrl);
                if (data.Length > 0)
                    return Convert.ToBase64String(data);
                else
                    return "";
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }


        /// <summary>
        /// 将剪报保存在本地
        /// </summary>
        /// <param name="doc"></param>
        /// <returns></returns>
        private bool saveClippingLocal(XmlDataDocument doc, string url, out string file)
        {
            string folder = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Clippings");
            try
            {
                if (!System.IO.Directory.Exists(folder))
                {
                    System.IO.Directory.CreateDirectory(folder);
                }

                string fileName = string.Format("{0}{1}", DateTime.Now.ToString("yyyyMMddhhmmss"), ClippingHelper.Md5Hash(url).Replace("-", ""));
                if (this.sysSetting.ZipClipping)
                {
                    var content = Encoding.GetEncoding(this.sysSetting.Encoding).GetBytes(doc.InnerXml);
                    file = System.IO.Path.Combine(folder, string.Format("{0}.zip", fileName));
                    using (ZipFile zip = new ZipFile())
                    {
                        zip.AddEntry(string.Format("{0}.xml", fileName), content);
                        zip.Save(file);
                    }
                }
                else
                {
                    file = System.IO.Path.Combine(folder, string.Format("{0}.xml", fileName));
                    System.IO.File.WriteAllText(file, doc.InnerXml, Encoding.GetEncoding(this.sysSetting.Encoding));
                }
                return true;
            }
            catch (Exception ex)
            {
                file = null;
                return false;
            }
        }


        /// <summary>
        /// 更新上传历史
        /// </summary>
        private void updateHistory()
        {
            try
            {
                Clipping.Utils.UploadHistoryRepository rep = new Clipping.Utils.UploadHistoryRepository();
                var links = this.news.Select(n => n.Url);
                foreach (var link in links)
                {


                    foreach (var category in this.categorySelector.SelectedCategories)
                    {
                        rep.AddHistory(
                            new UploadHistory()
                            {
                                Url = link,
                                ClientId = category.Id
                            }
                             );
                    }
                }
            }
            catch (Exception ex)
            {
                this.logHelper.LogErrMsg(ex, "更新上传历史");
            }
        }




        private void btn_Cancel_Click(object sender, RoutedEventArgs e)
        {
            this.Uploaded = false;
            this.Close();
        }

        public bool Uploaded { get; private set; }
    }

    public class NewsModel : GalaSoft.MvvmLight.ObservableObject
    {
        public NewsModel(News news)
        {
            this.News = news;

        }

        public News News { get; private set; }

        private string _status;

        public string Status
        {
            get { return _status; }
            set
            {
                if (this._status == value)
                    return;
                _status = value;
                RaisePropertyChanged(() => this.Status);
            }
        }

        public bool IsReady { get; set; }
        public bool Complete { get; set; }
    }
}
